#include <ltPtcParse.h>
#include <typeConvert.h>
#include <ltCmdList.h>
#include <genrand.h>
#include <crcCalc.h>
// 0xb5 for not ascii byte begin
static DEV_C51_CODE_FLAG const uint8_t parseFixedHead[LT_PTC_PACK_PST_VERS] = {0XB5, 0XD7}; // fixed head save

static void ptcLtPsRestart(struct ltPtcPsStc *parseIstc)
{
    parseIstc->parseState = ltPtcPsSt_Begin;
}

ltPtcLenType ltPtcPackFinV1(void *ptcPtr)
{
    struct ltPtcPsStc *parseIstc = (struct ltPtcPsStc *)ptcPtr;
    ltPtcSetCrc(parseIstc, calcXorCrc8(ltPtcGetDataPtrV1(parseIstc), ltPtcGetDataSizeV1(parseIstc)));
    memcpy(parseIstc->pkptr, parseFixedHead, sizeof(parseFixedHead));
    ltPtcSetVers(parseIstc, ltPtcPsVers1);
    return ltPtcGetPackSizeV1(parseIstc);
}

static int ltPtcChkCrcV1(void *ptcPtr)
{
    uint8_t tmpcrc;
    tmpcrc = calcXorCrc8(ltPtcGetDataPtrV1(ptcPtr), ltPtcGetDataSizeV1(ptcPtr));
    if (tmpcrc == ltPtcGetCrc(ptcPtr))
    { // ok
        return 0;
    }
    logerror("%x:%x", tmpcrc, ltPtcGetCrc(ptcPtr));
    return -1;
}
int ltPtcInitV1(void *ptcPtr, ltPtcLenType setPackSize)
{
    struct ltPtcPsStc *parseIstc = (struct ltPtcPsStc *)ptcPtr;
    if (!(parseIstc))
    {
        logerror("");
        return -1;
    }
    if ((setPackSize < (ltPtcPsComSize + ltPtcGetHeadSizeV1)))
    {
        logerror("");
        return -1;
    }
    parseIstc->totalsize = setPackSize - ltPtcPsComSize;
#if LT_PTC_CMD_USE_CRYPTE
    memcpy(parseIstc->ltPtcKeyPre, LT_PTC_PRE_FIX_SM4_KEY, sizeof(parseIstc->ltPtcKeyPre));
    grdStrNorm(parseIstc->ltPtcKeyDlg, sizeof(parseIstc->ltPtcKeyDlg));
#endif
    ptcLtPsRestart(parseIstc);
    return 0;
}
int ltPtcPsDataV1(void *ptcPtr, uint8_t indata)
{
    int tmpflag = 0;
    struct ltPtcPsStc *parseIstc = (struct ltPtcPsStc *)ptcPtr;
    switch (parseIstc->parseState)
    {
    case ltPtcPsSt_Begin:
    {
        parseIstc->parseState = ltPtcPsSt_FixHd;
        parseIstc->rcvCount = 0;
        memset(parseIstc->pkptr, 0, parseIstc->totalsize);
    }
    case ltPtcPsSt_FixHd:
    {
        if (parseIstc->rcvCount < sizeof(parseFixedHead))
        {
            if (indata == parseFixedHead[parseIstc->rcvCount])
            {

                parseIstc->pkptr[parseIstc->rcvCount++] = indata;
            }
            else
            {
                parseIstc->rcvCount = 0;
                if (indata == parseFixedHead[parseIstc->rcvCount])
                {
                    parseIstc->pkptr[parseIstc->rcvCount++] = indata;
                }
            }
        }
        if (parseIstc->rcvCount >= sizeof(parseFixedHead))
        {
            parseIstc->parseState = ltPtcPsSt_Vers;
        }
        break;
    }
    case ltPtcPsSt_Vers:
    {
        parseIstc->pkptr[parseIstc->rcvCount++] = indata;
        if (!(ltPtcGetVers(parseIstc) == ltPtcPsVers1))
        {
            logerror("parseVersion:%d!=%d", ltPtcPsVers1, ltPtcGetVers(parseIstc));
            ptcLtPsRestart(parseIstc);
            break;
        }
        parseIstc->needSize = ltPtcGetHeadSizeV1;
        if (!parseIstc->needSize)
        {
            logerror("parseVersion:%d", ltPtcGetHeadSizeV1);
            ptcLtPsRestart(parseIstc);
            break;
        }
        parseIstc->parseState = ltPtcPsSt_Hd;
        break;
    }
    case ltPtcPsSt_Hd:
    {
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            parseIstc->pkptr[parseIstc->rcvCount++] = indata;
        }
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            break;
        }
        parseIstc->needSize = ltPtcGetPackSizeV1(parseIstc);
        if (!(parseIstc->needSize >= ltPtcGetHeadSizeV1 &&
              parseIstc->needSize <= parseIstc->totalsize))
        {
            logerror("%u,%u", parseIstc->needSize, parseIstc->totalsize);
            ptcLtPsRestart(parseIstc);
            break;
        }
        parseIstc->parseState = ltPtcPsSt_Data;
        break;
    }
    case ltPtcPsSt_Data:
    {
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            parseIstc->pkptr[parseIstc->rcvCount++] = indata;
        }
        if (parseIstc->rcvCount < parseIstc->needSize)
        {
            break;
        }
        if (ltPtcChkCrcV1(parseIstc) < 0)
        {
            logerror("");
            ptcLtPsRestart(parseIstc);
            break;
        }
        // parse ok!
        // ptcPsOptCbk(parseIstc);
        tmpflag = 1;
    }
    default:
        ptcLtPsRestart(parseIstc);
        break;
    }
    return tmpflag;
}
#if LT_PTC_CMD_USE_CRYPTE
int chkPrmKeyOk(const uint8_t *chkkey, int keysize)
{
    int i = 0;
    for (i = 0; i < keysize; i++)
    {
        if (chkkey[i] != 0)
        {
            return 0;
        }
    }
    return -1;
}
#endif
#if LT_PTC_CMD_SUB_ENABLE
static int ltPtcSubDealCmd(void *inptr)
{
    struct ltPtcPsStc *tmpPtc = (struct ltPtcPsStc *)inptr;
#if LT_PTC_CMD_USE_CRYPTE
    const uint8_t *tmpkey = (const uint8_t *)ltPtcGetKeyDlgPtr(inptr);
#endif
    uint8_t tmpvalue;
    tmpvalue = ltPtcGetAddrV1(inptr);
    if (tmpvalue == LT_CMD_BOARDCAST_ADDR)
    {
        // check boardcast
        switch (ltPtcGetCmdIdV1(inptr))
        {
        case ltCmdEnumDef(rwSn):
        case ltCmdEnumDef(rwAddr):
        {
            break;
        }
        default:
        {
            logdebug("cmd not surpport boardcast:%d", ltPtcGetCmdIdV1(inptr));
            return -1;
        }
        }
    }
    else
    {
        // check addr
        if (tmpvalue != parmNormIstc.addr485)
        {
            logdebug("");
            return -1;
        }
    }

#if LT_PTC_CMD_USE_CRYPTE
    tmpkey = (const uint8_t *)ltPtcGetKeyDlgPtr(inptr);
    switch (ltPtcGetCrypteV1(inptr))
    {
    case ltPtcCrypte_Sm4Pre:
    {
        tmpkey = (const uint8_t *)ltPtcGetKeyPrePtr(inptr);
    }
    case ltPtcCrypte_Sm4Dlg:
    {
        if (chkPrmKeyOk(tmpkey, sizeof(tmpkey)) < 0)
        {
            logerror("key not set:%d", ltPtcGetCrypteV1(inptr));
            tmpPtc->subRespStatFlag = ltPtcCmdResp_KeyErr;
            goto cmdSndBackPtr;
        }
        if (ltPtcGetDataSizeV1(inptr) % SM4_BLOCK_SIZE)
        {
            logerror("crypte data size error:%d,%d", ltPtcGetCrypteV1(inptr), ltPtcGetDataSizeV1(inptr));
            tmpPtc->subRespStatFlag = ltPtcCmdResp_CryptErr;
            goto cmdSndBackPtr;
        }
        sm4DecryptePro1(ltPtcGetDataPtrV1(inptr), ltPtcGetDataSizeV1(inptr), tmpkey);
        tmpvalue = calcXorCrc8(ltPtcGetDataPtrV1(inptr), ltPtcGetDataSizeV1(inptr));
        if (tmpvalue != ltPtcGetCrc1V1(inptr))
        {
            logerror("crypte data crc error:%d,%d", tmpvalue, ltPtcGetCrc1V1(inptr));
            tmpPtc->subRespStatFlag = ltPtcCmdResp_CryptCrcErr;
            goto cmdSndBackPtr;
        }
        break;
    }
    default:
        break;
    }
#else
    if (ltPtcGetCrypteV1(inptr) != ltPtcCrypte_None)
    {
        logerror("key not set:%d", ltPtcGetCrypteV1(inptr));
        tmpPtc->subRespStatFlag = ltPtcCmdResp_CryptErr;
        goto cmdSndBackPtr;
    }
#endif
    // logdebug("");
    tmpPtc->subRespSizeHostCmdId = 0;
    tmpPtc->subRespStatFlag = ltPtcCmdResp_OK;
    tmpPtc->subRespCrypteHostAddr = ltPtcCrypte_None;

    ltCmdSubDealByID(inptr);

    if (tmpPtc->subRespStatFlag != ltPtcCmdResp_OK)
    {
        logerror("cmd result flag error:%d,%d", tmpPtc->subRespStatFlag, ltPtcGetCmdIdV1(inptr));
        goto cmdSndBackPtr;
    }
    if(tmpPtc->subRespSizeHostCmdId > ltPtcGetDataMaxSizeV1(inptr))
    {
        logerror("cmd result flag error:%d,%d", tmpPtc->subRespStatFlag, ltPtcGetCmdIdV1(inptr));
        tmpPtc->subRespStatFlag = ltPtcCmdResp_LenErr;
        goto cmdSndBackPtr;
    }

#if LT_PTC_CMD_USE_CRYPTE
    tmpkey = (const uint8_t *)ltPtcGetKeyDlgPtr(inptr);
    switch (tmpPtc->subRespCrypteHostAddr)
    {
    case ltPtcCrypte_Sm4Pre:
    {
        tmpkey = (const uint8_t *)ltPtcGetKeyPrePtr(inptr);
    }
    case ltPtcCrypte_Sm4Dlg:
    {
        if (chkPrmKeyOk(tmpkey, sizeof(tmpkey)) < 0)
        {
            logerror("key not set:%d", tmpPtc->subRespCrypteHostAddr);
            tmpPtc->subRespStatFlag = ltPtcCmdResp_KeyErr;
            goto cmdSndBackPtr;
        }
        tmpPtc->subRespSizeHostCmdId = (tmpPtc->subRespSizeHostCmdId + SM4_BLOCK_SIZE - 1) / SM4_BLOCK_SIZE * SM4_BLOCK_SIZE;
        if (tmpPtc->subRespSizeHostCmdId > ltPtcGetDataMaxSizeV1(inptr))
        {
            logerror("resp size error:%d, %d", tmpPtc->subRespCrypteHostAddr, tmpPtc->subRespSizeHostCmdId);
            tmpPtc->subRespStatFlag = ltPtcCmdResp_LenErr;
            goto cmdSndBackPtr;
        }
        ltPtcSetCrc1V1(inptr, calcXorCrc8(ltPtcGetDataPtrV1(inptr), tmpPtc->subRespSizeHostCmdId));
        sm4EncryptePro1(ltPtcGetDataPtrV1(inptr), tmpPtc->subRespSizeHostCmdId, tmpkey);
        break;
    }
    default:
    {
        ltPtcSetCrypteV1(inptr, tmpPtc->subRespCrypteHostAddr);
        break;
    }
    }
#else
    tmpPtc->subRespCrypteHostAddr = ltPtcCrypte_None;
#endif
cmdSndBackPtr:

    if (tmpPtc->subRespStatFlag == ltPtcCmdResp_NotSndBack)
    {
        logdebug("cmd:%d, not need response", ltPtcGetCmdIdV1(inptr));
        return 0;
    }
    else if (tmpPtc->subRespStatFlag != ltPtcCmdResp_OK)
    {
        ltPtcSetDataSizeV1(inptr, 0);
        ltPtcPackFinV1(inptr);
    }
    else
    {
        ltPtcSetDataSizeV1(inptr, tmpPtc->subRespSizeHostCmdId);
        ltPtcPackFinV1(inptr);
    }
    return ltPtcGetPackSizeV1(inptr);
}
int ltPtcSubPsAndDeal(void *inptr, char indata)
{
    struct ltPtcPsStc *tmpopt = (struct ltPtcPsStc *)inptr;
    if (ltPtcPsDataV1(inptr, indata) <= 0)
    {
        return 0;
    }
    // logdebug("");
    return ltPtcSubDealCmd(tmpopt);
}
#endif

#if LT_PTC_CMD_HOST_ENABLE
int ltPtcHostPackCommV1(void *inptr, uint8_t saddr, uint8_t scrypte, uint16_t cmdid, ltPtcLenType cmdlen)
{
    struct ltPtcPsStc *tmpPtc = (struct ltPtcPsStc *)inptr;
    tmpPtc->subRespCrypteHostAddr = saddr; // save cmd addr
    tmpPtc->subRespSizeHostCmdId = cmdid;       // save cmd id
    ltPtcSetAddrV1(inptr, saddr);
    ltPtcSetCmdIdV1(inptr, cmdid);
    ltPtcSetDataSizeV1(inptr, cmdlen);
    ltPtcSetResponseV1(inptr, 0);
#if LT_PTC_CMD_USE_CRYPTE
    const uint8_t *tmpkey = (const uint8_t *)ltPtcGetKeyDlgPtr(inptr);
    ltPtcSetCrypteV1(inptr, scrypte);
    switch (ltPtcGetCrypteV1(inptr))
    {
    case ltPtcCrypte_Sm4Pre:
    {
        tmpkey = (const uint8_t *)ltPtcGetKeyPrePtr(inptr);
    }
    case ltPtcCrypte_Sm4Dlg:
    {
        if (chkPrmKeyOk(tmpkey, sizeof(tmpkey)) < 0)
        {
            logerror("key not set:%d", ltPtcGetCrypteV1(inptr));
            return 0;
        }
        cmdlen = (cmdlen + SM4_BLOCK_SIZE - 1) / SM4_BLOCK_SIZE * SM4_BLOCK_SIZE;
        if (cmdlen > ltPtcGetDataMaxSizeV1(inptr))
        {
            logerror("resp size error:%d, %d", scrypte, cmdlen);
            return 0;
        }
        ltPtcSetCrc1V1(inptr, calcXorCrc8(ltPtcGetDataPtrV1(inptr), cmdlen));
        sm4EncryptePro1(ltPtcGetDataPtrV1(inptr), cmdlen, tmpkey);
        ltPtcSetDataSizeV1(inptr, cmdlen);
        break;
    }
    default:
        break;
    }
#else
    ltPtcSetCrypteV1(inptr, ltPtcCrypte_None);
#endif
    return ltPtcPackFinV1(inptr);
}
extern int ltCmdHostDealByID(struct ltPtcPsStc *ptcptr);
// return : <0 parse error, 0:cmdid,addr not pair, 1:ok, >1 response error ltCmdRespEnum
static int ptcLtHostDoResp(void *inptr)
{
    struct ltPtcPsStc *tmpPtc = (struct ltPtcPsStc *)inptr;
    int tmpflag = 1;

#if LT_PTC_CMD_USE_CRYPTE
    const uint8_t *tmpkey = (const uint8_t *)ltPtcGetKeyDlgPtr(inptr);
    uint8_t tmpvalue;
    switch (ltPtcGetCrypteV1(inptr))
    {
    case ltPtcCrypte_Sm4Pre:
    {
        tmpkey = (const uint8_t *)ltPtcGetKeyPrePtr(inptr);
    }
    case ltPtcCrypte_Sm4Dlg:
    {
        if (chkPrmKeyOk(tmpkey, sizeof(tmpkey)) < 0)
        {
            logerror("key not set:%d", ltPtcGetCrypteV1(inptr));
            tmpflag = -1;
            goto hostDoRespErrPtr;
        }
        if (ltPtcGetDataSizeV1(inptr) % SM4_BLOCK_SIZE)
        {
            logerror("crypte data size error:%d,%d", ltPtcGetCrypteV1(inptr), ltPtcGetDataSizeV1(inptr));
            tmpflag = -1;
            goto hostDoRespErrPtr;
        }
        sm4DecryptePro1(ltPtcGetDataPtrV1(inptr), ltPtcGetDataSizeV1(inptr), tmpkey);
        tmpvalue = calcXorCrc8(ltPtcGetDataPtrV1(inptr), ltPtcGetDataSizeV1(inptr));
        if (tmpvalue != ltPtcGetCrc1V1(inptr))
        {
            logerror("crypte data crc error:%d,%d", tmpvalue, ltPtcGetCrc1V1(inptr));
            tmpflag = -1;
            goto hostDoRespErrPtr;
        }
        break;
    }
    default:
        break;
    }
#else
    if (ltPtcGetCrypteV1(inptr) != ltPtcCrypte_None)
    {
        logerror("key not set:%d", ltPtcGetCrypteV1(inptr));
        tmpflag = -1;
        goto hostDoRespErrPtr;
    }
#endif
    // response error
    if(ltPtcGetResponseV1(tmpPtc) > 0)
    {
        tmpflag = ltPtcGetResponseV1(tmpPtc);
        goto hostDoRespErrPtr;
    }

    tmpflag = ltCmdHostDealByID(tmpPtc);

hostDoRespErrPtr:
    if (tmpPtc->subRespSizeHostCmdId != ltPtcGetCmdIdV1(tmpPtc))
    {
        logwarn("diff cmd id:%d,%d", tmpPtc->subRespSizeHostCmdId, ltPtcGetCmdIdV1(tmpPtc));
        tmpflag = 0;
    }
    else if (tmpPtc->subRespCrypteHostAddr != ltPtcGetAddrV1(tmpPtc))
    {
        logwarn("diff cmd addr:%d,%d", tmpPtc->subRespCrypteHostAddr, ltPtcGetAddrV1(tmpPtc));
        tmpflag = 0;
    }
    return tmpflag;
}
// return:
// 0:wait response, 1:ok, <0:error
int ltPtcHostPsAndDeal(void *inptr, char indata)
{
    struct ltPtcPsStc *tmpopt = (struct ltPtcPsStc *)inptr;
    if (ltPtcPsDataV1(inptr, indata) <= 0)
    {
        return 0;
    }
    return ptcLtHostDoResp(tmpopt);
}
#endif
